package com.thientan.controller

import java.text.SimpleDateFormat

import org.hibernate.FetchMode
import org.hibernate.StaleObjectStateException
import org.hibernate.exception.ConstraintViolationException
import org.springframework.dao.DataIntegrityViolationException
import org.springframework.dao.OptimisticLockingFailureException

import grails.converters.JSON

import com.thientan.export.ExportType
import com.thientan.model.DuLieuHeThong
import com.thientan.model.HopDong
import com.thientan.model.KhachHang
import com.thientan.model.NhanVien
import com.thientan.model.ThongKePhieuThu
import com.thientan.model.enums.LoaiDuLieu
import com.thientan.model.enums.LoaiNhanVien
import com.thientan.model.enums.TrangThaiHopDong
import com.thientan.service.HopDongService
import com.thientan.service.ThongKePhieuThuService
import com.thientan.utils.Constants
import com.thientan.utils.Utility

class HopDongController extends AbstractBaseController {
	HopDongService hopDongService
	ThongKePhieuThuService thongKePhieuThuService

	@Override
	public Class getDomainClass() {
		return HopDong.class
	}

	@Override
	public String getDomainName() {
		message(code : "default.thientan.messages.hopDong")
	}

	@Override
	public Object getDomainService() {
		return hopDongService
	}

	@Override
	public String getDisplayFieldName() {
		"maHd"
	}
	
	protected String getLogoPath() {
		"images${slash}logo.png"
	} 

	def initData = {
		Map result = ["success" : true]		
		List<NhanVien> doiTruongs = NhanVien.findAllWhere("doiTruong" : null, "kichHoat" : true, "loaiNhanVien" : LoaiNhanVien.BAN_HANG)
		List<NhanVien> nvBan = NhanVien.findAllWhere("kichHoat" : true, "loaiNhanVien" : LoaiNhanVien.BAN_HANG)
		List<NhanVien> thuNgans = NhanVien.findAllWhere("kichHoat" : true, "loaiNhanVien" : LoaiNhanVien.THU_NGAN)
		List<DuLieuHeThong> tinhThanhs = DuLieuHeThong.findAllByLoaiDuLieu(LoaiDuLieu.TINH_THANH)
		result.putAt("doiTruong", extractPropertiesAsMaps(doiTruongs, ["id", "maNv"]))
		result.putAt("nvBan", extractPropertiesAsMaps(nvBan + thuNgans, ["id", "maNv", "doiTruong.id"]))
		result.putAt("thuNgan", extractPropertiesAsMaps(thuNgans, ["id", "maNv"]))
		result.putAt("tinhThanh", extractPropertiesAsMaps(tinhThanhs, ["maDuLieu", "giaTri"]))

		if (params["isSearchScreen"]) {
			List trangThais = TrangThaiHopDong.values().collect {
				["value" : it.name(), "text" : it.toString()]
			}
			result.putAt("trangThais", trangThais)
		}
		render result as JSON
	}

	def reloadQuanHuyenByThanhPho = {
		List<DuLieuHeThong> quanHuyens = DuLieuHeThong.findAllByLoaiDuLieuAndMaDuLieuLienQuan(
				LoaiDuLieu.QUAN_HUYEN, params["maTinh"])
		render extractPropertiesAsMaps(quanHuyens, ["maDuLieu", "giaTri"]) as JSON
	}

	def reloadPhuongXaByQuanHuyen = {
		List<DuLieuHeThong> phuongXas = DuLieuHeThong.findAllByLoaiDuLieuAndMaDuLieuLienQuan(LoaiDuLieu.PHUONG_XA, params["maQuanHuyen"])
		render extractPropertiesAsMaps(phuongXas, ["maDuLieu", "giaTri"]) as JSON
	}
	
	def reloadDuLieuThanhPho = {
		List<DuLieuHeThong> tinhThanhs = DuLieuHeThong.findAllByLoaiDuLieu(LoaiDuLieu.TINH_THANH)
		render extractPropertiesAsMaps(tinhThanhs, ["maDuLieu", "giaTri"]) as JSON
	}

	@Override
	protected Map prepareParams() {
		Map parameters = params
		
		if (!params["id"]) {// Just calculate money info only when creating new HopDong
			parameters.putAt("maHd", params["maHd"].toUpperCase())
			parameters.putAt("soHd", Long.valueOf(params["maHd"].substring(3)))
			parameters.putAt("giaTri", params.double("giaTri") * Constants.RATE)
			parameters.putAt("tienTraTruoc", params.double("tienTraTruoc") * Constants.RATE)
			if (params["ketThuc"]) {
				parameters.put("tienGiamGia", parameters["giaTri"] - parameters["tienTraTruoc"])
				parameters.put("tienDaThu", 0)
				parameters.put("tienConLai", 0)
				parameters.put("trangThai", parameters["tienGiamGia"] == 0 ? TrangThaiHopDong.TIEN_MAT : TrangThaiHopDong.KET_THUC)
			} else if (params["thuTienNgay"]) {
				parameters.put("tienDaThu", 0)
				parameters.put("tienConLai", 0)
				parameters.put("trangThai", TrangThaiHopDong.TIEN_MAT)
			} else {
				parameters.putAt("tienDaThu", params.double("tienDaThu") * Constants.RATE)
				parameters.putAt("tienConLai", params.double("tienConLai") * Constants.RATE)
				if (parameters["tienConLai"] == 0 && actionName == "save") {
					parameters.put("trangThai", TrangThaiHopDong.KET_THUC)
				}
			}
			
			KhachHang kh = new KhachHang(parameters)
			parameters.putAt("khachHang", kh)
		} else {
			if (params["ketThuc"]) {
				parameters.put("tienGiamGia", params.double("tienConLai") * Constants.RATE)
				parameters.put("tienConLai", 0)
				parameters.put("trangThai", TrangThaiHopDong.KET_THUC)
				parameters.remove("giaTri")
				parameters.remove("tienTraTruoc")
				parameters.remove("tienDaThu")
			} else {
				parameters.putAt("giaTri", params.double("giaTri") * Constants.RATE)
				parameters.putAt("tienTraTruoc", params.double("tienTraTruoc") * Constants.RATE)
				parameters.putAt("tienDaThu", params.double("tienDaThu") * Constants.RATE)
				parameters.putAt("tienConLai", params.double("tienConLai") * Constants.RATE)
			}
		}
		
		parameters.put("nvDoiTruong.id", params["nvDoiTruong-id"])
		parameters.put("nvBan1.id", params["nvBan1-id"])
		parameters.put("nvBan2.id", params["nvBan2-id"])
		parameters.put("nvThuNgan.id", params["nvThuNgan-id"])
		parameters.remove("nvDoiTruong-id")
		parameters.remove("nvBan1-id")
		parameters.remove("nvBan2-id")
		parameters.remove("nvThuNgan-id")
		parameters.putAt("ngayBan", new SimpleDateFormat(Constants.DEFAULT_DATE_FORMAT).parse(params["ngayBan"]))
		
		parameters
	}
	
	// Override this function to increase count PhieuThu when create new hopdong
	protected void updateAdditionalInfoAfterSave(def object){
		ThongKePhieuThu tkPt = thongKePhieuThuService.fetchOrCreateThongKePt()
		thongKePhieuThuService.updateThongKePhieuThu(tkPt, object, 1 /*numHd*/, new Date())
		if (tkPt.errors.hasErrors()) {
			log.error("Unable to update ThongKePhieuThu when input PhieuThu: ${tkPt.errors}")
			throw new Exception("default.thientan.messages.updateTkPtFailed")
		}				
	}
	
	def edit = {
		String msg
		def objectId = params['id']
		HopDong hd = domainClass.get(objectId)
		
		// Binding params.
		
		Map returnMap = [:]
		try {
			Map parameters = prepareParams()
			hd.properties = parameters
			KhachHang kh = hd.khachHang
			kh.properties = parameters
			def savedObject = hopDongService.edit(hd, kh)
			if(!hd.errors.allErrors){
				// Update editing object in session
				putOrUpdateToSession(savedObject, savedObject.id)
				msg = message(code : 'default.thientan.messages.saveSuccess',
						args : [
							domainName,
							hd.getAt(displayFieldName)
						])
				returnMap = getFormMessage(true, "info", msg, null)
				returnMap += renderSavedDomain(hd)
			} else {
				log.warn('Binding/Validation errors happended during saving: ' + hd.errors.allErrors)
				def errors = getErrorList(hd.errors.allErrors, domainName)
				msg = message(code : "default.action.save.fail.who",
						args : [
							getDomainName(),
							params.getAt(displayFieldName)
						])
				returnMap = getFormMessage(false, "error", msg, errors)
			}
		} catch(OptimisticLockingFailureException ex) {
			log.error("OptimisticLockingFailureException happened during saving", ex)
			returnMap = produceConcurrentUpdateMessage(displayFieldName)
		} catch(StaleObjectStateException ex) {
			log.error("StaleObjectStateException happened during saving", ex)
			returnMap = produceConcurrentUpdateMessage(displayFieldName)
		} catch(DataIntegrityViolationException ex) {
			log.error("DataIntegrityViolationException happened during saving", ex)
			returnMap = produceConstraintViolationMessage()
		} catch(ConstraintViolationException ex) {
			log.error("ConstraintViolationException happened during saving", ex)
			returnMap = produceConstraintViolationMessage()
		} catch(Exception ex){
			log.error("Unexpected error happened during saving", ex)
			returnMap = handleSaveExceptions(ex, returnMap)
		}
		returnMap["customInfo"] = customInfo
		render (returnMap as JSON)
	}	
	

	def requestHdInfo = {
		String maHd = params["maHd"]
		List<HopDong> hds = HopDong.withCriteria {
			if (params["isEditHd"]) {
				fetchMode("khachHang", FetchMode.JOIN)
			}
			eq("maHd", maHd.toUpperCase())
		}
		Map result = [:]
		if(hds) {
			HopDong hd = hds.first()
			if (Boolean.valueOf(params["isEditHd"]) == true) {
				Map data = extractPropertiesAsMap(hd, [
					"id",
					"maHd",
					"nvBan1.maNv",
					"nvBan2.maNv",
					"nvDoiTruong.maNv",
					"nvThuNgan.maNv",
					"ngayBan",
					"giaTri",
					"dotBan",
					"tienTraTruoc",
					"tienDaThu",
					"tienConLai",
					"trangThaiText",
					"daGiaoPhieu",
					"khachHang.hoTen",
					"khachHang.diaChi",
					"khachHang.dienThoai",
					"khachHang.phuongXa",
					"khachHang.quanHuyen",
					"khachHang.tinhThanh"
				])
				try {
					List<DuLieuHeThong> qhs = DuLieuHeThong.findAll("from DuLieuHeThong d where d.loaiDuLieu='QUAN_HUYEN' and d.maDuLieuLienQuan=" +
						"(select t.maDuLieu from DuLieuHeThong t where t.loaiDuLieu='TINH_THANH' and t.giaTri='${hd.khachHang.tinhThanh}') " +
							"order by d.giaTri")
					List<DuLieuHeThong> pxs = DuLieuHeThong.findAll("from DuLieuHeThong d where d.loaiDuLieu='PHUONG_XA' and d.maDuLieuLienQuan=" +
							"(select qh.maDuLieu from DuLieuHeThong qh where qh.loaiDuLieu='QUAN_HUYEN' and qh.giaTri='${hd.khachHang.quanHuyen}' " +
							"and qh.maDuLieuLienQuan=(select t.maDuLieu from DuLieuHeThong t where t.loaiDuLieu='TINH_THANH' and t.giaTri='${hd.khachHang.tinhThanh}'))"+
							"order by d.giaTri")
					data.putAt("quanHuyen", extractPropertiesAsMaps(qhs, ["maDuLieu", "giaTri"]))
					data.putAt("phuongXa", extractPropertiesAsMaps(pxs, ["maDuLieu", "giaTri"]))
				} catch (Exception e) {
					data.putAt("quanHuyen", [])
					data.putAt("phuongXa", [])
				}
				
				result = ["success" : true, "data" : data]
			} else {
				if (!hd.nvThuNgan) {
					result = ["success" : false, 
							  "msg" : message(code : "default.thientan.messages.notAssignThuNgan", args : null)]				
				} else {
					Map data = extractPropertiesAsMap(hd, [
						"id",
						"maHd",
						"nvBan1.maNv",
						"nvBan2.maNv",
						"nvDoiTruong.maNv",
						"nvThuNgan.maNv",
						"ngayBan",
						"giaTri",
						"tienTraTruoc",
						"tienDaThu",
						"tienConLai",
						"trangThaiText"
					])
					result = ["success" : true, "data" : data]
				}
			}
		} else {
			result = ["success" : false, 
					  "msg" : message(code : "default.thientan.messages.hopDongNotExist", args : null)]
		}
		render result as JSON
	}
	
	def checkStatus = {
		HopDong hd = HopDong.get(params["id"])
		Map result = ['isTraGop' : hd.trangThai == TrangThaiHopDong.DANG_TRA_GOP]
		render result as JSON
	}
	
	@Override
	protected Map buildExportData(Map exportParams){
		// Collect hopDong info to export Phieu Thu
		HopDong hopDong = HopDong.get(params['id'])
		String maHd = hopDong ? hopDong.maHd : ""
		
		// Generate barcode
		String barcodePath = "${realPath}exported${slash}barcode${maHd}.gif"
		Utility.generateBarcode(maHd, barcodePath)
		
		// Prepare data for export
		List phieuThuData = (1..24).collect{item ->
			['stt' : item,
			 'ngaytra' : "", 'tienthu' : "", 'tienconno' : "", 'khky' : "", 'tnky' : "", 'tenthungan' : ""
			]
		}
		
		return ['data' : phieuThuData,
				'ngayin' : printedCurrentDay,
				'tenkhachhang' : hopDong?.khachHang.hoTen ?: "",
				'diachi' : hopDong?.khachHang.fullDiaChi ?: "",
				'dienthoai' : hopDong?.khachHang.dienThoai ?: "",
				'tongtien' : Utility.moneyFormat(hopDong?.giaTri ?: 0),
				'doitruong' : hopDong?.nvDoiTruong?.maNv ?: "",
				'nv1' : hopDong?.nvBan1?.maNv ?: "",
				'nv2' : hopDong?.nvBan2?.maNv ?: "",
				'tratruoc' : Utility.moneyFormat(hopDong?.tienTraTruoc ?: 0),
				'conlai' : Utility.moneyFormat(hopDong?.tienConLai ?: 0),
				'mahopdong' : maHd,
				'mavachpath' : barcodePath,
				'logopath' : realPath + logoPath
				]
	}
	
	@Override
	protected String getExportFileName(){
		"PhieuThu"
	}
	
	@Override
	protected ExportType getExportType(){
		ExportType.PDF
	}
}